export interface HttpResponse<T = any> extends Response {
    status: number;
    message?: string;
    data?: T;
    [key: string | number | symbol]: any;
}

/**
 * Represents an error that occurs during an HTTP request.
 */
export class HttpError extends Error {
    public status: number;

    constructor(message: string, status: number) {
        super(message);
        this.name = 'HttpError';
        this.status = status;
    }
}

export default class Http {
    public static HttpError = HttpError;

    private static async statusHandler(response: any, method?: 'GET' | 'POST' | 'DELETE' | 'PUT', callback?: (response: Response) => void): Promise<HttpResponse> {
        if (!response.ok) {
            return await response.json() || {};
            throw new HttpError(`HTTP request(${response.url}) error status: ${response.status}`, response);
        }

        switch (response.status) {
            case 200:
                // For other responses, return the JSON body
                const responseBody: HttpResponse = await response.json();
                if(Array.isArray(responseBody)){
                    return responseBody;
                }
                
                if (Object.prototype && responseBody.status && responseBody.status !== 200) {
                    base.toastmgr.show(`HTTP ${method} request failed: ${responseBody.message}`, 'danger');

                    //throw new HttpError(responseBody.message || 'Unknown error', responseBody.status);
                }

                return responseBody || {};
            case 201:
                // Created response, return the response body
                return await response.json();
            case 202:
                // Created response, return the response body
                const result = await response.json();
                return await this.waitingTaskId(result, response, method, callback);
            case 204:
                // No content response, return an empty object
                return { status: 204, message: 'No Content', data: null } as HttpResponse;
            default:
                // For other responses, return the JSON body
                // Bad request response, throw an error with the response body
                const errorBody = await response.json();
                return errorBody || {};
        }

        // If the response does not match expected structure, throw an error
        throw new HttpError('Unexpected response status: ', response.status);
    }

    private static async waitingTaskId(result: any, resp: HttpResponse, method?: 'GET' | 'POST' | 'DELETE' | 'PUT', callback?: (response: Response) => void): Promise<HttpResponse> {
        const _taskId = result.task_id;
        if (!_taskId) {
            return result;
        }
        let isCompleted = false;
        while (!isCompleted) {
            try {
                let _formdata_ = new FormData();
                _formdata_.append('task_id', _taskId);
                const response = await fetch(`${resp.url}`, { method: method || 'POST', body: _formdata_ });
                callback && callback(response);
                switch (response.status) {
                    case 200:
                        // 处理成功
                        isCompleted = true;
                        return await response.json();
                    case 202:
                        // 继续轮询
                        await new Promise(resolve => setTimeout(resolve, 2000)); // 延迟2秒
                        break;
                    default:
                        // 处理其他状态码
                        isCompleted = true;
                        break;
                }
            } catch (err) {
                // 处理错误
                isCompleted = true;
                console.error(err);
            }
        }

        return result; // 延迟2秒
    }

    private static async fetchWithParams(url: string, params?: Record<string, any>, isEncode?: boolean): Promise<Response> {
        if (!params) {
            return fetch(url, {
                method: 'GET',
                headers: {
                    'Content-Type': 'application/json',
                }
            });
        }
        // Convert params to query starting with '?'
        // This is necessary to ensure the URL is properly formatted
        // and to avoid issues with special characters in the URL.
        if (typeof params !== 'object' || Array.isArray(params)) {
            throw new TypeError('Params must be an object');
        }

        // Create a query string from the params object
        // This will handle encoding and formatting correctly.
        // URLSearchParams is a built-in utility for creating query strings.
        // It automatically encodes the keys and values, ensuring that special characters are handled properly.         
        if (Object.keys(params).length === 0) {
            // If params is empty, just return the URL without query string
            return fetch(url, {
                method: 'GET',
                headers: {
                    'Content-Type': 'application/json',
                }
            });
        }

        if (isEncode) {
            for (const param in params) {
                params[param] = encodeURIComponent(params[param]);
            }
        }

        // Create a query string from the params object
        const query = new URLSearchParams(params).toString();
        const response = await fetch(`${url}?${query}`, {
            method: 'GET',
            headers: {
                'Content-Type': 'application/json'
            }
        });

        return response;
    }

    static async get(url: string, params?: Record<string, any>, isEncode?: boolean): Promise<HttpResponse> {
        try {
            const response = await this.fetchWithParams(url, params, isEncode);

            return await this.statusHandler(response, 'GET');
        } catch (error: any) {
            console.error('HTTP GET request failed:', error);
            base.toastmgr.show(`HTTP GET request failed: ${error.message}`, 'danger');
            throw error;
        }
    }

    static async delete(url: string, body?: Record<string, any>): Promise<HttpResponse> {
        try {
            const query = new URLSearchParams(body).toString();
            const response = await fetch(`${url}?${decodeURIComponent(query)}`, {
                method: 'DELETE',
                headers: {
                    'Content-Type': 'application/json'
                }
            });

            return await this.statusHandler(response, 'DELETE');
        } catch (error: any) {
            console.error('HTTP DELETE request failed:', error);
            base.toastmgr.show(`HTTP DELETE request failed: ${error.message}`, 'danger');
            throw error;
        }
    }

    static async post(url: string, body?: Record<string, any>): Promise<HttpResponse> {
        try {
            const response = await fetch(url, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify(body)
            });

            return await this.statusHandler(response, 'POST');
        } catch (error: any) {
            console.error('HTTP POST request failed:', error);
            base.toastmgr.show(`HTTP POST request failed: ${error.message}`, 'danger');
            throw error;
        }
    }

    static async put(url: string, body?: Record<string, any>): Promise<HttpResponse> {
        try {
            const response = await fetch(url, {
                method: 'PUT',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify(body)
            });

            return await this.statusHandler(response, 'PUT');
        } catch (error: any) {
            console.error('HTTP PUT request failed:', error);
            base.toastmgr.show(`HTTP PUT request failed: ${error.message}`, 'danger');
            throw error;
        }
    }

    static async uploads(url: string, formData?: Record<string, any>, onProgress?: (progress: number, event: Event) => void): Promise<HttpResponse> {
        try {
            let _formdata_ = new FormData();
            if (formData instanceof FormData) {
                _formdata_ = formData;
            } else {
                for (let key in formData) {
                    if (Array.isArray(formData[key])) {
                        for (const file of formData[key]) {
                            if (file instanceof File) {
                                _formdata_.append(key, file);
                            }
                        }
                    } else {
                        _formdata_.append(key, formData[key]);
                    }
                }
            }
            const response = await new Promise((resolve, reject) => {
                const xhr = new XMLHttpRequest();
                xhr.open("POST", url);
                //xhr.setRequestHeader('Content-Type', 'application/json');
                xhr.upload.onprogress = (event) => {
                    let _progress = parseFloat((event.loaded / event.total).toFixed(2)) * 100;
                    onProgress && onProgress(_progress, event);
                };
                xhr.upload.onabort = () => {
                    reject("已取消上传");
                };
                xhr.onerror = (err: any) => {
                    reject("上传失败！");
                    resolve({ message: err.message || '上传失败', status: 400 } as HttpResponse);
                };
                xhr.onabort = () => {
                    reject("已取消上传");
                    resolve({ message: "已取消上传", status: 400 } as HttpResponse);
                };
                xhr.onload = () => {
                    switch(xhr.status) {
                        case 200:
                            resolve(JSON.parse(xhr.responseText) as HttpResponse);
                            break;
                        default:
                            resolve(JSON.parse(xhr.responseText) as HttpResponse);
                    }
                };

                xhr.send(_formdata_);
            }).catch((e) => {
                throw new Error(e);
            });

            return response as HttpResponse;
        } catch (err: any) {
            console.error('HTTP uploadFiles request failed:', err);
            base.toastmgr.show(`HTTP uploadFiles request failed: ${err.message}`, 'danger');
            throw err;
        }
    }

    static async formdata(url: string, formData?: Record<string, any>, callback?: (response: Response, progress: number) => void): Promise<HttpResponse> {
        try {
            let _formdata_ = new FormData();
            if (formData instanceof FormData) {
                _formdata_ = formData;
            } else {
                for (let key in formData) {
                    if (Array.isArray(formData[key])) {
                        for (const file of formData[key]) {
                            if (file instanceof File) {
                                _formdata_.append(key, file);
                            }
                        }
                    } else {
                        _formdata_.append(key, formData[key]);
                    }
                }
            }

            // Step 1：启动 fetch，并获得一个 reader
            const response = await fetch(url, {
                method: 'POST',
                body: _formdata_
            });
            const reader = response.body?.getReader();

            // Step 2：获得总长度（length）
            const contentLength = parseInt(response.headers?.get('Content-Length') || '0', 10);

            // Step 3：读取数据
            let receivedLength = 0; // 当前接收到了这么多字节
            let chunks = []; // 接收到的二进制块的数组（包括 body）
            while (true) {
                if (!reader) {
                    break;
                }
                const readResult = await reader.read();
                const { done, value } = readResult;
                if (done) {
                    break;
                }

                if (value) {
                    chunks.push(value);
                    receivedLength += value.length;
                }
                console.log(`Received ${receivedLength} of ${contentLength}`)
                callback && callback(response, receivedLength / contentLength);
            }

            // Step 4：将块连接到单个 Uint8Array
            let chunksAll = new Uint8Array(receivedLength); // (4.1)
            let position = 0;
            for (let chunk of chunks) {
                chunksAll.set(chunk, position); // (4.2)
                position += chunk.length;
            }

            // Step 5：解码成字符串
            let result = new TextDecoder("utf-8").decode(chunksAll);
            let _obj = JSON.parse(result);

            return _obj;

            //return await this.statusHandler(response, 'POST', callback);
        } catch (error: any) {
            console.error('HTTP FormData request failed:', error);
            base.toastmgr.show(`HTTP FormData request failed: ${error.message}`, 'danger');
            throw error;
        }
    }
    static async readStream(url: string, body?: any, successCallback?: (data: any)=>void, errorCallback?: (err: any)=>void): Promise<any> {
        let _options: RequestInit = {
            method: 'POST',
            body: new FormData()
        };
        if(body) {
            switch(body.__proto__.toString()){
                case '[object FormData]':
                    _options.body = body;
                    break;
                case '[object Object]':
                    _options.headers = {
                        'Content-Type': 'application/json'
                    };
                    _options.body = JSON.stringify(body);
                    break;
            }
        }
        
        try {
            const response = await fetch(url, _options);
            if (!response.ok) {
                const result = await response.json();
                throw new Error(result.message || result.error || '请求出错');
            }
            if (!response.body) {
                throw new Error('请求出错');
            }
            if(response.status === 202) {
                const result = await response.json();
                return await this.waitingTaskId(result, response, 'POST', successCallback);
            }

            // 创建响应流读取器
            const reader = response.body.getReader();
            const decoder = new TextDecoder();
            let buffer = '';
            while (true) {
                const { value, done } = await reader.read();
                if (done) {
                    // 处理缓冲区中剩余的数据
                    if (buffer.length > 0) {
                        try {
                            const data = JSON.parse(buffer);
                            successCallback && successCallback.call(this, data);
                            return data;
                        } catch (e) {
                            console.error('Failed to parse remaining output:', e);
                        }
                    }
                    break;
                }

                // 将新数据添加到缓冲区
                buffer += decoder.decode(value || '', { stream: true });

                // 处理完整的行
                let newlineIndex;
                while ((newlineIndex = buffer.indexOf('\n')) !== -1) {
                    const line = buffer.slice(0, newlineIndex);
                    buffer = buffer.slice(newlineIndex + 1);
                    if (line.trim()) {
                        try {
                            const data = JSON.parse(line);
                            successCallback && successCallback.call(this, data);
                        } catch (e) {
                            console.error('Failed to parse output line:', e);
                        }
                    }
                }
            }
        } catch (err: any) {
            errorCallback && errorCallback(err);
            console.error('HTTP POST request failed:', err);
            base.toastmgr.show(`HTTP POST request failed: ${err.message}`, 'danger');
            throw err;
        }
    }
}